const express = require('express');
const spdy = require('spdy');
const multer = require('multer');
const fs = require('fs-extra');
const path = require('path');
const { createClient } = require('redis');

const upload = multer().any();
const app = express();
app.use(express.json());

app.use(express.static(__dirname + '/public'));

const client = createClient();

app.get('/file/:name', (req, res) => {
    const file = path.resolve(__dirname, 'files', req.params.name);
    const readStream = fs.createReadStream(file);
    readStream.pipe(res);
});

app.post('/upload', (req, res) => {
    upload(req, res, async err => {
        if (err) res.send(500, err.message);
        const { chunkName, hash } = req.body;
        const exist = await client.get(chunkName);
        if (exist) {
            res.send({ msg: chunkName + '已经存在' });
            return;
        }
        const filename = path.resolve(__dirname, 'files', chunkName);
        await fs.writeFile(filename, req.files[0].buffer);
        await client.set(chunkName, hash);
        res.status(206).send(chunkName + '上传完毕');
    });
});

app.post('/merge', async (req, res) => {
    const { filename, size, length, hash } = req.body;
    const exits = await hashExists('fileHash', hash);
    if (exits) {
        console.log('文件已存在，直接返回');
        return;
    }
    await mergeFile(filename, length, size, hash);
    await addHash(hash, 'fileHash');
    res.send({ fileURL: '/file/' + filename });
});

app.post('/check', async (req, res) => {
    const { hash } = req.body;
    const exits = await hashExists('fileHash', hash);
    res.send({ exits });
});

function pipeStream(filePath, writeSteam) {
    return new Promise((resolve, reject) => {
        const readStream = fs.createReadStream(filePath);
        readStream.on('end', async () => {
            await fs.unlink(filePath);
            readStream.close();
            resolve();
        });
        readStream.on('error', err => reject(err));
        readStream.pipe(writeSteam);
    });
}

async function mergeFile(filename, length, size) {
    const chunks = [];
    for (let i = 0; i < length; i++) {
        const chunkName = `${i}-${filename}`;
        chunks.push(path.resolve(__dirname, 'files', chunkName));
        await client.del(chunkName);
    }
    const output = path.resolve(__dirname, 'files', filename);
    return Promise.all(
        chunks.map((chunk, index) => {
            pipeStream(
                chunk,
                fs.createWriteStream(output, {
                    start: index * size,
                    end: (index + 1) * size
                })
            );
        })
    );
}

(async () => {
    // 目录不存在，创建目录
    if (!fs.pathExistsSync(path.resolve(__dirname, 'files'))) {
        fs.mkdirSync(path.resolve(__dirname, 'files'));
    }
    await client.connect();
    client.on('error', err => console.log('Redis Client Error', err));
})();

async function addHash(hash, field) {
    let hashList = await client.get(field);
    hashList = JSON.parse(hashList);
    console.log(hashList);
    hashList.push(hash);
    await client.set(field, JSON.stringify(hashList));
}

async function hashExists(field, hash) {
    let hashList = await client.get(field);
    if (hashList === null) {
        hashList = [];
        await client.set(field, JSON.stringify(hashList));
    }
    return hashList.indexOf(hash) > -1;
}

spdy.createServer(
    {
        key: fs.readFileSync(__dirname + '/server.key'),
        cert: fs.readFileSync(__dirname + '/server.crt')
    },
    app
).listen(3000, err => {
    if (err) throw new Error(err);

    console.log('server has started on https://localhost:3000');
});
